#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <memory.h>
#include <time.h>
#include "avcodec.h"
#include "internal.h"
#include "DRAPlusDec.h"

#define __min(a,b) (((a) < (b)) ? (a) : (b))

typedef struct DRAContext
{
	void *pDRADecoder;
}DRAContext;

static av_cold int dra_decode_init(AVCodecContext *avctx)
{
    struct DRAContext *s = avctx->priv_data;

	avctx->frame_size = 1024;
	avctx->channels = 0;
	avctx->sample_fmt = AV_SAMPLE_FMT_S16;

	s->pDRADecoder = DRAPlusDecCreate();
	if(s->pDRADecoder == NULL)
	{
		return 1;
	}

	return 0;
}

static int dra_decode_frame(AVCodecContext *avctx, void *data,
                                 int *got_frame_ptr, AVPacket *avpkt)
{
	int ret = 0;
	
    uint8_t *buf = avpkt->data;
    int buf_size = avpkt->size;
	AVFrame *frame     = data;
    int dec_len = 0;
	DRAPlusFrameInfo dra_frame_info;
	
    struct DRAContext *s = avctx->priv_data;
	
	unsigned char *pcm_data = NULL;
	int pcm_len = 0;

    *got_frame_ptr = 0;

	if(buf == NULL || buf_size == 0)
	{
		ret = DRAPlusDecRecvFrame(s->pDRADecoder, &pcm_data, &pcm_len);
		if(ret == 0)
		{
			DRAPlusDecGetFrameInfo(s->pDRADecoder, &dra_frame_info);
			avctx->sample_rate = dra_frame_info.nSampleRate;
			avctx->channels = dra_frame_info.nChannels;
			avctx->channel_layout = av_get_default_channel_layout(avctx->channels);
			if(avctx->channels != 0)
			{
				avctx->frame_size = pcm_len / (avctx->channels * 2);
			}

			frame->channels = avctx->channels;
			frame->channel_layout = avctx->channel_layout;
			frame->sample_rate = avctx->sample_rate;
			frame->nb_samples = avctx->frame_size;
			frame->format = avctx->sample_fmt;

		    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
		        return ret;

		    memcpy(frame->data[0], pcm_data, pcm_len);

		    *got_frame_ptr = 1;
		}
		dec_len = buf_size;
	}
	else
	{
		ret = DRAPlusDecRecvFrame(s->pDRADecoder, &pcm_data, &pcm_len);
		if(ret == 0)
		{
			DRAPlusDecGetFrameInfo(s->pDRADecoder, &dra_frame_info);
			avctx->sample_rate = dra_frame_info.nSampleRate;
			avctx->channels = dra_frame_info.nChannels;
			avctx->channel_layout = av_get_default_channel_layout(avctx->channels);
			if(avctx->channels != 0)
			{
				avctx->frame_size = pcm_len / (avctx->channels * 2);
			}

			frame->channels = avctx->channels;
			frame->channel_layout = avctx->channel_layout;
			frame->sample_rate = avctx->sample_rate;
			frame->nb_samples = avctx->frame_size;
			frame->format = avctx->sample_fmt;

		    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
		        return ret;

		    memcpy(frame->data[0], pcm_data, pcm_len);

		    *got_frame_ptr = 1;

		    dec_len = 0;
		}
		else
		{
			ret = DRAPlusDecSendData(s->pDRADecoder, buf, buf_size);
			if(ret != 0)
			{
				return AVERROR(AVERROR_BUG);
			}

			ret = DRAPlusDecRecvFrame(s->pDRADecoder, &pcm_data, &pcm_len);
			if(ret == 0)
			{
				DRAPlusDecGetFrameInfo(s->pDRADecoder, &dra_frame_info);
		    	avctx->sample_rate = dra_frame_info.nSampleRate;
		    	avctx->channels = dra_frame_info.nChannels;
		    	avctx->channel_layout = av_get_default_channel_layout(avctx->channels);
		    	if(avctx->channels != 0)
		    	{
		    		avctx->frame_size = pcm_len / (avctx->channels * 2);
		    	}

		    	frame->channels = avctx->channels;
		    	frame->channel_layout = avctx->channel_layout;
				frame->sample_rate = avctx->sample_rate;
		    	frame->nb_samples = avctx->frame_size;
		    	frame->format = avctx->sample_fmt;

			    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
			        return ret;

			    memcpy(frame->data[0], pcm_data, pcm_len);

			    *got_frame_ptr = 1;
			}

			dec_len = buf_size;
		}
	}
	
	return dec_len;
}

static av_cold int dra_decode_end(AVCodecContext *avctx)
{
	struct DRAContext *s = avctx->priv_data;

	if(s->pDRADecoder)
	{
		DRAPlusDecDestroy(s->pDRADecoder);
		s->pDRADecoder = NULL;
	}
	
    return 0;
}

AVCodec ff_libdraplus_decoder = {
    .name           = "libdraplus",
    .type           = AVMEDIA_TYPE_AUDIO,
    .id             = AV_CODEC_ID_DRA,
    .priv_data_size = sizeof (DRAContext),
    .init           = dra_decode_init,
    .close          = dra_decode_end,
    .decode         = dra_decode_frame,
    .capabilities   = AV_CODEC_CAP_CHANNEL_CONF |
                      AV_CODEC_CAP_DR1,
    .long_name      = NULL_IF_CONFIG_SMALL("dra audio decoder"),
    .sample_fmts    = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16,
                                                      AV_SAMPLE_FMT_NONE },
    .priv_class     = NULL,
    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
};
